package com.hmkj.core.api.ufx.call;

import com.hmkj.boot.utils.SpringContextUtils;
import com.hmkj.core.api.ufx.mc.UFXMC;
import com.hmkj.core.api.ufx.trade.UfxTradeSync;
import com.hmkj.core.constant.CacheID;
import com.hmkj.core.constant.RiskNID;
import com.hmkj.core.exception.BussinessException;
import com.hmkj.core.model.quote.KlineData;
import com.hmkj.core.model.quote.TrendData;
import com.hmkj.core.po.commo.Commo;
import com.hmkj.core.service.cache.RedisService;
import com.hmkj.core.service.risk.RiskParamService;
import com.hundsun.mcapi.MCServers;
import com.hundsun.t2sdk.impl.client.T2Services;
import com.xiaoleilu.hutool.date.DateUtil;
import com.xiaoleilu.hutool.util.StrUtil;

import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 期货行情相关请求实例
 */
public class QuotationCall {

    private static T2Services server = T2Services.getInstance();//T2实例化

    private static UFXMC ufxmc = new UFXMC();

    private static UfxTradeSync ufxTradeSync = new UfxTradeSync();

    private static QuotationCall quotationCall = new QuotationCall();

    private static Map<String, List<String>> tenderTimeMap;


    private QuotationCall() {
        tenderTimeMap = new HashMap<>();
        // 初始化国内期货行情时间
        initTendTimes4In();
    }


    public static QuotationCall getInstance() {
        return quotationCall;
    }

    /**
     * 获取T2SDK配置文件中配置的连接
     * 启动T2SDK，只需要在服务器启动时调用一次就可以了
     */
    public void initT2() throws Exception {
        server.setT2sdkConfigString("classpath:config/t2sdk_hundsun_quote-config.xml");//配置文件路径
        server.init();
        server.start();
        MCServers.MCInit();
    }

    /**
     * 断开服务
     */
    public void stop() {
        server.stop();
    }

    /**
     * 行情主推订阅
     */
    public void reqSubQuotation() {
        // 订阅国际期货行情
        ufxmc.reqFunctionSubQuoOut();
        // 订阅国内期货行情
        ufxmc.reqFunctionSubQuoIn();

    }

    /**
     * 查询国际期货分时行情
     *
     * @param futuExchType  交易所
     * @param commodityType 品种代码
     * @param contractCode  合约号
     * @param requestNum    请求条数
     * @return List<TrendData>
     */
    public List<TrendData> getTrendDataOut(String futuExchType, String commodityType, String contractCode, String requestNum) throws Exception {
        ufxTradeSync.Init();
        String result = ufxTradeSync.ReqFunction800050(futuExchType, commodityType, contractCode, requestNum);
        //返回信息如下：
        /*
            contract_code,futu_exch_type,commodity_type,min_time,date,last_price,average_price,business_amount,business_balance,min_amount,
            1709,NYMEX,CL,2222,20170817,46.850000,46.710000,268373,12535712.740000,1025,
         */
        String[] data = result.split("\n");
        List<TrendData> trendDataList = new LinkedList<>();
        for (int i = data.length - 1; i > 0; i--) {
            String[] args = data[i].split(",");
            TrendData trendData = new TrendData();
            trendData.setCc(args[0]);
            trendData.setFet(args[1]);
            trendData.setCt(args[2]);
            trendData.setMt(args[3]);
            trendData.setD(args[4]);
            trendData.setNp(args[5]);
            trendData.setAp(args[6]);
            trendData.setBa(args[7]);
            trendData.setBb(args[8]);
            trendData.setMa(args[9]);
            trendData.setHp(args[10]);
            trendData.setLp(args[11]);
            trendData.setOp(args[12]);
            trendData.setCp(args[13]);
            trendDataList.add(trendData);
        }
        return trendDataList;
    }

    /**
     * 查询国内期货分时行情
     *
     * @param commodityType 品种代码
     * @param contractCode  合约号
     * @return List<TrendData>
     */
    public List<TrendData> getTrendDataIn(String commodityType, String contractCode) throws Exception {
        ufxTradeSync.Init();
        String result = ufxTradeSync.ReqFunction3003(commodityType + contractCode);
        //返回信息如下：
        /*
            last_price,business_amount,
            46.850000,46,
         */
        String[] data = result.split("\n");
        List<TrendData> trendDataList = new LinkedList<>();
        SimpleDateFormat f1 = new SimpleDateFormat("yyyyMMddHHmm");
        SimpleDateFormat f2 = new SimpleDateFormat("yyyyMMdd");
        Date now = new Date();
        String dateStr = f2.format(now);
        List<String> tenderTimeList = tenderTimeMap.get(commodityType);

        String preDateStr = f2.format(DateUtil.offsetDay(now, -1));

        // 前分时点总量
        long pba = 0L;

        for (int i = 1; i < data.length; i++) {
            String[] args = data[i].split(",");
            TrendData trendData = new TrendData();
            trendData.setCc(contractCode);
            trendData.setCt(commodityType);
            trendData.setNp(args[0]);
            trendData.setBa(args[1]);

            trendData.setD(dateStr);
            trendData.setMt(tenderTimeList.get(i - 1));
            if (DateUtil.offsetMinute(now, 1).before(f1.parse(dateStr + trendData.getMt()))) {
                trendData.setD(preDateStr);
            }
            trendData.setDt(f1.parse(trendData.getD() + trendData.getMt()).getTime());

            trendData.setMa(String.valueOf(Long.parseLong(args[1]) - pba));

            trendDataList.add(trendData);

            pba = Long.parseLong(trendData.getBa());
        }


//        // 总量
//        long totalVol = 0L;
//        // 总金额
//        BigDecimal totalBalance = BigDecimal.ZERO;
//        List<TrendData> datas = new ArrayList<>();
//        for (int i = trendDataList.size() - 1; i >= 0; i--) {
//            TrendData item = trendDataList.get(i);
//            // 分时量
//            long curVol = Long.parseLong(item.getBa()) - totalVol;
//            item.setBa(String.valueOf(curVol));
//            // 分时成交金额
//            BigDecimal curBalance = new BigDecimal(item.getNp()).multiply(new BigDecimal(curVol));
//
//            totalVol = Long.parseLong(item.getBa());
//            totalBalance = totalBalance.add(curBalance);
//            // 计算均价
//            BigDecimal avgPrice = totalBalance.divide(new BigDecimal(totalVol), 2, BigDecimal.ROUND_HALF_UP);
//            item.setAp(String.valueOf(avgPrice));
//            datas.add(item);
//
//        }
        return trendDataList;
    }

    public void qryMarket() throws Exception {
        ufxTradeSync.Init();
        String result = ufxTradeSync.ReqFunction3007();
        System.out.println(result);

    }

    /**
     * 内盘期货初始化分时数据节点
     */
    private static void initTendTimes4In() {
        try {
            Date now = new Date();
            SimpleDateFormat f1 = new SimpleDateFormat("yyyyMMddHHmm");
            SimpleDateFormat f2 = new SimpleDateFormat("yyyyMMdd");
            SimpleDateFormat f3 = new SimpleDateFormat("HHmm");

            RedisService redisService = SpringContextUtils.getBean("redisService");
            List<Commo> list = redisService.getList(CacheID.COMMO_LIST, Commo.class);

            for (Commo commo : list) {
                if (commo.getType().intValue() == Commo.TYPE.T2.code) {
                    List<String[]> periodList = new ArrayList<>();
                    RiskParamService riskParamService = SpringContextUtils.getBean("riskParamServiceImpl");
                    String quoteTimeStr = riskParamService.getParamValue(RiskNID.QUOTE_TIME, commo.getId());
                    String[] datas = quoteTimeStr.split(";");

                    // 夜盘数据在前面
                    String nightTimes = datas[datas.length - 1];
                    String[] nightItems = nightTimes.split("\\|");
                    for (int j = 0; j < nightItems.length; j++) {
                        String[] nightPeriods = nightItems[j].split(",");
                        nightPeriods[0] = nightPeriods[0].replace(":", "");
                        nightPeriods[1] = nightPeriods[1].replace(":", "");
                        periodList.add(nightPeriods);
                    }

                    // 日盘的数据在后面
                    for (int i = 0; i < datas.length - 1; i++) {
                        String[] items = datas[i].split("\\|");
                        for (int j = 0; j < items.length; j++) {
                            String[] periods = items[j].split(",");
                            periods[0] = periods[0].replace(":", "");
                            periods[1] = periods[1].replace(":", "");
                            periodList.add(periods);
                        }
                    }

                    List<String> tenderTimes = new ArrayList<>();
                    for (String[] periods : periodList) {
                        Date s = f1.parse(f2.format(now) + periods[0]);
                        Date e = f1.parse(f2.format(now) + periods[1]);
                        if (s.after(e)) {
                            e = DateUtil.offsetDay(e, 1);
                        }
                        while (true) {
                            if (s.getTime() > e.getTime()) {
                                break;
                            }
                            tenderTimes.add(f3.format(s));
                            s = DateUtil.offsetMinute(s, 1);
                        }
                    }
                    tenderTimeMap.put(commo.getCode(), tenderTimes);
                }

            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 获取日k数据
     *
     * @param futuExchType  交易所
     * @param commodityType 品种代码
     * @param contractCode  合约号
     * @param requestNum    请求条数
     * @return List<KlineData>
     * @throws Exception ex
     */
    public List<KlineData> getDKlineData(String futuExchType, String commodityType, String contractCode, String requestNum) throws Exception {
        ufxTradeSync.Init();
        String result = ufxTradeSync.ReqFunction800061(futuExchType, commodityType, contractCode, requestNum);
        /*
        business_amount,close_price,commodity_type,contract_code,date,futu_exch_type,high_price,low_price,open_price,
        815365,47.620000,CL,1710,20170824,NYMEX,48.420000,47.080000,48.350000,
        692639,48.370000,CL,1710,20170823,NYMEX,48.470000,47.560000,47.640000,
        850342,47.630000,CL,1710,20170822,NYMEX,48.180000,47.360000,47.600000,
         */
        String[] data = result.split("\n");
        List<KlineData> klineDataList = new LinkedList<>();

        for (int i = data.length - 1; i > 0; i--) {
            String[] args = data[i].split(",");
            KlineData klineData = new KlineData();
            klineData.setCc(contractCode);
            klineData.setFet(futuExchType);
            klineData.setCt(commodityType);
            klineData.setBa(args[0]);
            klineData.setCp(args[1]);
            klineData.setD(args[2]);
            klineData.setHp(args[3]);
            klineData.setLp(args[4]);
            klineData.setOp(args[5]);
            klineDataList.add(klineData);
        }
        return klineDataList;
    }

    /**
     * 获取k线数据
     *
     * @param futuExchType  交易所
     * @param commodityType 品种代码
     * @param contractCode  合约号
     * @param type          k线类型（3--3分钟，5--5分钟，15--15分钟，30--30分钟，60--1时）
     * @return List<KlineData>
     * @throws Exception ex
     */
    public List<KlineData> getKlineData(String futuExchType, String commodityType, String contractCode, String type) throws Exception {
        ufxTradeSync.Init();
        String result = ufxTradeSync.ReqFunction800062(futuExchType, commodityType, contractCode, type);
        /*
            date,min_time,high_price,low_price,open_price,close_price,min_amount,
            20170908,632,49.1000,49.0900,49.1000,49.1000,34,
            20170908,635,49.1000,49.0900,49.1000,49.0900,105,
         */
        String[] data = result.split("\n");
        List<KlineData> klineDataList = new LinkedList<>();
        for (int i = 1; i < data.length; i++) {
            String[] args = data[i].split(",");
            KlineData klineData = new KlineData();
            klineData.setCc(contractCode);
            klineData.setFet(futuExchType);
            klineData.setCt(commodityType);
            klineData.setD(args[0]);
            klineData.setMt(args[1]);
            klineData.setHp(args[2]);
            klineData.setLp(args[3]);
            klineData.setOp(args[4]);
            klineData.setCp(args[5]);
            klineData.setBa(args[6]);
            klineDataList.add(klineData);
        }
        return klineDataList.size() > 90 ? klineDataList.subList(klineDataList.size() - 90, klineDataList.size()) : klineDataList;
    }


    /**
     * 国内期货获取k线数据
     *
     * @param positionStr 定位串
     * @param code        品种代码
     * @param contract    合约号
     * @param period      k线周期 1:1分钟；2:5分钟；3:15分钟；4:30分钟；5:60分钟；6:日K；7:周K；8:月K；
     * @return List<KlineData>
     * @throws Exception ex
     */
    public List<KlineData> getKlineDataNative(String positionStr, String code, String contract, int period) throws Exception {
        ufxTradeSync.Init();
        String result = ufxTradeSync.ReqFunction3002(positionStr, code.toUpperCase() + contract, String.valueOf(period));

        /*
            date,       business_amount,    open_price, close_price,    high_price, low_price,  business_balance,
            2803131331, 1762,               272.30,     272.30,         272.40,     272.20,     334258,//1分钟
            2803122250, 2258,               271.75,     271.85,         271.90,     271.65,     334896,//5分钟
            2803090230, 1794,               273.30,     273.30,         273.30,     273.10,     336336,//15分钟
            2803070000, 10836,              274.85,     275.00,         275.15,     274.75,     334276,//30分钟
            2802282200, 13180,              272.35,     271.95,         272.45,     271.80,     327968,//60分钟
            20171031,   42752,              277.70,     278.55,         279.45,     277.70,     121898,//日K
            20170519,   52,                 282.65,     285.10,         287.10,     282.55,     36,    //周K
            20170531,   208,                282.65,     284.80,         287.95,     282.55,     80,    //月K
         */
        String[] data = result.split("\n");
        List<KlineData> klineDataList = new LinkedList<>();
        for (int i = 1; i < data.length; i++) {
            String[] args = data[i].split(",");
            KlineData klineData = new KlineData();
            klineData.setCc(code);
            klineData.setBa(args[1]);
            klineData.setOp(args[2]);
            klineData.setCp(args[3]);
            klineData.setHp(args[4]);
            klineData.setLp(args[5]);
            klineData.setBb(args[6]);
            if (period < 6) {
                // 分钟K线需要进行日期处理  2803131331  前两位表示年偏移量  需要加上1990，后面是日期时分
                String yearOffsetStr = args[0].substring(0, 2);
                String monthDayStr = args[0].substring(2, 6);
                String yearStr = String.valueOf(Integer.parseInt(yearOffsetStr) + 1990);
                klineData.setD(yearStr + monthDayStr);
                klineData.setMt(args[0].substring(6, 10));

            } else {
                klineData.setD(args[0]);
            }
            klineDataList.add(klineData);
        }
        return klineDataList.size() > 90 ? klineDataList.subList(klineDataList.size() - 90, klineDataList.size()) : klineDataList;
    }


    /**
     * 设置国内期货主推代码
     *
     * @param contractCode 合约代码
     * @return List<TrendData>
     */
    public void setPushCode(String contractCode) throws Exception {
        ufxTradeSync.Init();
        String result = ufxTradeSync.ReqFunction3005(contractCode);
        //返回信息如下：
        /*
            error_info,error_no
            1709,NYMEX,CL,2222,20170817,46.850000,46.710000,268373,12535712.740000,1025,
         */
        Map<String, String> data = analysisData(result);
        if (StrUtil.isNotBlank(data.get("error_no"))) {
            throw new BussinessException(data.get("error_info"));
        }
    }


    /**
     * 整理返回数据
     *
     * @param result
     * @return
     */
    private Map<String, String> analysisData(String result) {
        // 接收数据数组
        String[] keys = result.split("\n")[0].split("\\|");
        String[] vals = result.split("\n")[1].split("\\|");

        Map<String, String> datas = new HashMap<>();
        for (int i = 0; i < keys.length; i++) {
            datas.put(keys[i], vals[i]);
        }
        return datas;
    }

}
